﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Weazel.Badger.Vhdl
{
  public class ComponentDeclaration : IDeclarativeItem
  {		
		/// <summary>
		/// Name of this component
		/// </summary>
		private string name;
		public string Name
		{
			get { return name; }
		}

		private Entity entity;
		public Entity Entity
		{
			get { return entity; }
		}

		/// <summary>
		/// Number of ports in this component
		/// </summary>
		private int count = 0;
		public int Count
		{
			get { return count; }	
		}

		/// <summary>
		/// List of port names
		/// </summary>
    private List<string> portNames = new List<string>();
		public List<string> PortNames
		{
			get { return portNames; }		
		}

		/// <summary>
		/// Returns the name of a port at a given position
		/// </summary>
		/// <param name="position"></param>
		/// <returns></returns>
		public string PortNameAtPosition(int position)
		{
			return positionalMap[position];
		}

		/// <summary>
		/// Returns the direction of the port at position
		/// </summary>
		/// <param name="position"></param>
		/// <returns></returns>
		public PortDirection DirectionAtPosition(int position)
		{
			string name = positionalMap[position];
			
			for (int i = 0; i < portNames.Count; i++)
				if (portNames[i] == name)
					return directions[i];

			throw new IndexOutOfRangeException();
		}

		/// <summary>
		/// Map mapping declaration positions to port names
		/// </summary>
		private Dictionary<int, string> positionalMap = new Dictionary<int, string>();

		/// <summary>
		/// List of types. One type for each port entry.
		/// </summary>
    private List<Vhdl.Types.Type> types = new List<Weazel.Badger.Vhdl.Types.Type>();

		/// <summary>
		/// Direction of ports. One entry for each port
		/// </summary>
    private List<PortDirection> directions = new List<PortDirection>();

    public ComponentDeclaration(string name, Entity entity)
    {
      this.name = name;
			this.entity = entity;
    }

    public void AddIn(string portName, Vhdl.Types.Type type, int position)
    {
      if (portNames.Contains(portName))
        throw new Exception(string.Format("port name '{0}' already in component declaration", portName));

      portNames.Add(portName);
      types.Add(type);
      directions.Add(PortDirection.In);

			if (position != -1)
			{
				if (positionalMap.ContainsKey(position))
					throw new Exception("position " + position + " already bound (" + portName + ")");

				positionalMap.Add(position, portName);
			}

      count++;
    }

    public void AddOut(string portName, Vhdl.Types.Type type, int position)
    {
      if (portNames.Contains(portName))
        throw new Exception(string.Format("port name '{0}' already in component declaration", portName));

      portNames.Add(portName);
      types.Add(type);
      directions.Add(PortDirection.Out);

			if (position != -1)
			{
				if (positionalMap.ContainsKey(position))
					throw new Exception("position " + position + " already bound by '" + portName + "' (" + positionalMap[position]);

				positionalMap.Add(position, portName);
			}

      count++;
    }

		/// <summary>
		/// Compares two components
		/// </summary>
		/// <param name="component">compoenent to compare</param>
		/// <returns>true iff same</returns>
		public bool CompareTo(ComponentDeclaration component)
		{
			if (component.name != name)
				return false;

			if (component.portNames.Count != portNames.Count)
				return false;

			for (int i = 0; i < component.portNames.Count; i++)
				if (component.portNames[i] != portNames[i])
					return false;

			if (component.types.Count != types.Count)
				return false;

			for (int i = 0; i < component.types.Count; i++)
				if (component.types[i] != types[i])
					return false;

			return true;
		}

		public void Write(System.CodeDom.Compiler.IndentedTextWriter writer)
    {
      writer.WriteLine("component {0}", name);
      writer.Indent++;

      writer.WriteLine("port (");
      writer.Indent++;

      for (int i = 0; i < count - 1; i++)
      {
        string direction =
          directions[i] == PortDirection.In ? "in" :
          directions[i] == PortDirection.Out ? "out" :
          "inout"; // not used (yet!)

        writer.WriteLine("{0} : {1} {2};", portNames[i], direction, types[i].ToString());
      }
      
      string dir =
        directions[count - 1] == PortDirection.In ? "in" :
        directions[count - 1] == PortDirection.Out ? "out" :
        "inout"; // not used (yet!)

      writer.WriteLine("{0} : {1} {2}", portNames[count - 1], dir, types[count - 1].ToString());

      writer.Indent--;
      writer.WriteLine(");");

      writer.Indent--;
      writer.WriteLine("end component;");
    }
  }
}
